package vip.liux.contracts.models.files;

import jakarta.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import vip.liux.contracts.repositories.files.DirectoryDescriptorRepository;
import vip.liux.contracts.repositories.files.FileDescriptorRepository;

import java.util.List;
import java.util.Objects;

@Component
public class DirectoryManager {

    protected final FileManager fileManager;
    protected final DirectoryDescriptorRepository directoryDescriptorRepository;
    protected final FileDescriptorRepository fileDescriptorRepository;

    public DirectoryManager(FileManager fileManager, DirectoryDescriptorRepository directoryDescriptorRepository, FileDescriptorRepository fileDescriptorRepository) {
        this.fileManager = fileManager;
        this.directoryDescriptorRepository = directoryDescriptorRepository;
        this.fileDescriptorRepository = fileDescriptorRepository;
    }

    public DirectoryDescriptor create(String name, @Nullable Long parentId, @Nullable String tenantId) {
        DirectoryDescriptor directoryDescriptor = directoryDescriptorRepository.findByName(name, parentId);
        if (directoryDescriptor != null) {
            throw new DirectoryAlreadyExistException(name);
        }

        return new DirectoryDescriptor(name, getParentDirectory(parentId), tenantId);
    }

    public void rename(DirectoryDescriptor directory, String newName) {
        if (StringUtils.equals(directory.getName(), newName)) {
            return;
        }
        checkTargetDirectory(directory.getParentId(), newName);

        directory.setName(newName);
    }

    public void move(DirectoryDescriptor directory, @Nullable Long newParentId) {
        if (Objects.equals(directory.getParentId(), newParentId)) {
            return;
        }
        checkTargetDirectory(newParentId, directory.getName());

        if (!isMovableToAsync(directory, newParentId)) {
            throw new InvalidMoveException();
        }

        directory.setParent(getParentDirectory(newParentId));
    }
    
    public void delete(Long id) {
        List<Long> allSubDirectoryIds = directoryDescriptorRepository.getChildren(id);
        for (Long directoryId : allSubDirectoryIds) {
            delete(directoryId);
        }

        fileManager.deleteAll(id);
        directoryDescriptorRepository.deleteById(id);
    }

    protected boolean isMovableToAsync(DirectoryDescriptor directory, @Nullable Long parentId) {
        if (parentId == null) {
            return true;
        }

        return !isParentOfAsync(directory.getId(), parentId);
    }

    /**
     * 检查该目录是否为子目录的父目录
     *
     * @param parentId parent directory id
     * @param childId  child directory id
     * @return true if the parent is a parent of the child
     */
    protected boolean isParentOfAsync(Long parentId, Long childId) {
        DirectoryDescriptor directory = directoryDescriptorRepository.findById(childId).orElseThrow();

        while (directory.getParent() != null) {
            if (Objects.equals(directory.getParentId(), parentId)) {
                return true;
            }
            directory = directory.getParent();
        }
        return false;
    }

    protected void checkTargetDirectory(Long parentId, String name) {

        DirectoryDescriptor target = directoryDescriptorRepository.findByName(name, parentId);
        if (target != null) {
            throw new DirectoryAlreadyExistException(name);
        }
    }

    protected DirectoryDescriptor getParentDirectory(Long parentId) {
        return parentId == null ? null : directoryDescriptorRepository.findById(parentId).orElse(null);
    }

    public boolean existsByParentId(Long id, boolean checkFilesAlso) {

        boolean containsDirectory = directoryDescriptorRepository.findByParent(id) > 0;

        if (!checkFilesAlso) {
            return containsDirectory;
        }

        boolean containsFiles = fileDescriptorRepository.findByDirectoryId(id) > 0;

        return containsDirectory || containsFiles;
    }
}
